<!DOCTYPE html>
<html lang=zh>
<head>
    <!-- so meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
    <meta name="description" content="本篇文章转载自https:&#x2F;&#x2F;juejin.cn&#x2F;post&#x2F;6844904100035821575，这里做了简单梳理，仅作个人笔记使用。 http报文结构http的报文结构是header+body的结构，具体： 起始行 + 头部 + 空行 + 实体 起始行对请求报文来说，起始行类似于这样: GET &#x2F;home HTTP&#x2F;1.1 也就是方法 + 路径 + http版本 对于响应报文来说，起始行类似于">
<meta property="og:type" content="article">
<meta property="og:title" content="http总结">
<meta property="og:url" content="https://danyuan30.gitee.io/2022/03/05/network/http/index.html">
<meta property="og:site_name" content="柯里的语法糖">
<meta property="og:description" content="本篇文章转载自https:&#x2F;&#x2F;juejin.cn&#x2F;post&#x2F;6844904100035821575，这里做了简单梳理，仅作个人笔记使用。 http报文结构http的报文结构是header+body的结构，具体： 起始行 + 头部 + 空行 + 实体 起始行对请求报文来说，起始行类似于这样: GET &#x2F;home HTTP&#x2F;1.1 也就是方法 + 路径 + http版本 对于响应报文来说，起始行类似于">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://codertzm.oss-cn-chengdu.aliyuncs.com/image-20220228112828121.png">
<meta property="og:image" content="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd728ba3840d~tplv-t2oaga2asx-watermark.awebp">
<meta property="og:image" content="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd78332368a0~tplv-t2oaga2asx-watermark.awebp">
<meta property="og:image" content="https://codertzm.oss-cn-chengdu.aliyuncs.com/v2-b61cab529fa31301bde290813b4587fc_1440w.jpg">
<meta property="og:image" content="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd924eaecb41~tplv-t2oaga2asx-watermark.awebp">
<meta property="og:image" content="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd97d0b1cf15~tplv-t2oaga2asx-watermark.awebp">
<meta property="article:published_time" content="2022-03-05T12:31:10.772Z">
<meta property="article:modified_time" content="2022-03-05T12:31:10.772Z">
<meta property="article:author" content="柯里的语法糖">
<meta property="article:tag" content="http">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://codertzm.oss-cn-chengdu.aliyuncs.com/image-20220228112828121.png">
    
    
      
        
          <link rel="shortcut icon" href="https://codertzm.oss-cn-chengdu.aliyuncs.com/favicon.ico">
        
      
      
        
          <link rel="icon" type="image/png" href="https://codertzm.oss-cn-chengdu.aliyuncs.com/favicon.ico" sizes="192x192">
        
      
      
        
          <link rel="apple-touch-icon" sizes="180x180" href="https://codertzm.oss-cn-chengdu.aliyuncs.com/favicon.ico">
        
      
    
    <!-- title -->
    <title>http总结</title>
    <!-- styles -->
    
<link rel="stylesheet" href="/css/style.css">

    <!-- persian styles -->
    
    <!-- rss -->
    
    
	<!-- mathjax -->
	
<meta name="generator" content="Hexo 6.0.0"></head>

<body class="max-width mx-auto px3 ltr">
    
      <div id="header-post">
  <a id="menu-icon" href="#" aria-label="Menu"><i class="fas fa-bars fa-lg"></i></a>
  <a id="menu-icon-tablet" href="#" aria-label="Menu"><i class="fas fa-bars fa-lg"></i></a>
  <a id="top-icon-tablet" href="#" aria-label="Top" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;"><i class="fas fa-chevron-up fa-lg"></i></a>
  <span id="menu">
    <span id="nav">
      <ul>
        <!--
       --><li><a href="/">首页</a></li><!--
     --><!--
       --><li><a href="/tags/">标签</a></li><!--
     --><!--
       --><li><a href="/categories/">分类</a></li><!--
     --><!--
       --><li><a href="/archives/">归档</a></li><!--
     --><!--
       --><li><a target="_blank" rel="noopener" href="http://github.com/twitzz">项目</a></li><!--
     --><!--
       --><li><a href="/about/">关于</a></li><!--
     --><!--
       --><li><a href="/search/">搜索</a></li><!--
     -->
      </ul>
    </span>
    <br/>
    <span id="actions">
      <ul>
        
        <li><a class="icon" aria-label="上一篇" href="/2022/03/07/frontend/JavaScript%E6%89%8B%E5%86%99%E9%A2%98/"><i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i></a></li>
        
        
        <li><a class="icon" aria-label="下一篇" href="/2022/03/05/JavaScript/iterator-generator/"><i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i></a></li>
        
        <li><a class="icon" aria-label="返回顶部" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i></a></li>
        <li><a class="icon" aria-label="分享文章" href="#"><i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i></a></li>
      </ul>
      <span id="i-prev" class="info" style="display:none;">上一篇</span>
      <span id="i-next" class="info" style="display:none;">下一篇</span>
      <span id="i-top" class="info" style="display:none;">返回顶部</span>
      <span id="i-share" class="info" style="display:none;">分享文章</span>
    </span>
    <br/>
    <div id="share" style="display: none">
      <ul>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.facebook.com/sharer.php?u=https://danyuan30.gitee.io/2022/03/05/network/http/"><i class="fab fa-facebook " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://twitter.com/share?url=https://danyuan30.gitee.io/2022/03/05/network/http/&text=http总结"><i class="fab fa-twitter " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.linkedin.com/shareArticle?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-linkedin " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://pinterest.com/pin/create/bookmarklet/?url=https://danyuan30.gitee.io/2022/03/05/network/http/&is_video=false&description=http总结"><i class="fab fa-pinterest " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=http总结&body=Check out this article: https://danyuan30.gitee.io/2022/03/05/network/http/"><i class="fas fa-envelope " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://getpocket.com/save?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-get-pocket " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://reddit.com/submit?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-reddit " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.stumbleupon.com/submit?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-stumbleupon " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://digg.com/submit?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-digg " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.tumblr.com/share/link?url=https://danyuan30.gitee.io/2022/03/05/network/http/&name=http总结&description="><i class="fab fa-tumblr " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=https://danyuan30.gitee.io/2022/03/05/network/http/&t=http总结"><i class="fab fa-hacker-news " aria-hidden="true"></i></a></li>
</ul>

    </div>
    <div id="toc">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84"><span class="toc-number">1.</span> <span class="toc-text">http报文结构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B5%B7%E5%A7%8B%E8%A1%8C"><span class="toc-number">1.1.</span> <span class="toc-text">起始行</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%B4%E9%83%A8"><span class="toc-number">1.2.</span> <span class="toc-text">头部</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A9%BA%E8%A1%8C"><span class="toc-number">1.3.</span> <span class="toc-text">空行</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9E%E4%BD%93"><span class="toc-number">1.4.</span> <span class="toc-text">实体</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E8%AF%B7%E6%B1%82%E6%96%B9%E6%B3%95"><span class="toc-number">2.</span> <span class="toc-text">http请求方法</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#GET%E5%92%8CPOST%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">2.1.</span> <span class="toc-text">GET和POST的区别</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#URI"><span class="toc-number">3.</span> <span class="toc-text">URI</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#URI%E7%BB%93%E6%9E%84"><span class="toc-number">3.1.</span> <span class="toc-text">URI结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#URI-%E7%BC%96%E7%A0%81"><span class="toc-number">3.2.</span> <span class="toc-text">URI 编码</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E7%8A%B6%E6%80%81%E7%A0%81"><span class="toc-number">4.</span> <span class="toc-text">http状态码</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1xx"><span class="toc-number">4.1.</span> <span class="toc-text">1xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2xx"><span class="toc-number">4.2.</span> <span class="toc-text">2xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3xx"><span class="toc-number">4.3.</span> <span class="toc-text">3xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4xx"><span class="toc-number">4.4.</span> <span class="toc-text">4xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5xx"><span class="toc-number">4.5.</span> <span class="toc-text">5xx</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E7%9A%84%E7%89%B9%E7%82%B9"><span class="toc-number">5.</span> <span class="toc-text">http的特点</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E7%9A%84%E7%BC%BA%E7%82%B9"><span class="toc-number">6.</span> <span class="toc-text">http的缺点</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A0%E7%8A%B6%E6%80%81"><span class="toc-number">6.1.</span> <span class="toc-text">无状态</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%98%8E%E6%96%87%E4%BC%A0%E8%BE%93"><span class="toc-number">6.2.</span> <span class="toc-text">明文传输</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%98%9F%E5%A4%B4%E9%98%BB%E5%A1%9E"><span class="toc-number">6.3.</span> <span class="toc-text">队头阻塞</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Accept%E7%B3%BB%E5%88%97%E5%AD%97%E6%AE%B5"><span class="toc-number">7.</span> <span class="toc-text">Accept系列字段</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F"><span class="toc-number">7.1.</span> <span class="toc-text">数据格式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%8B%E7%BC%A9%E6%96%B9%E5%BC%8F"><span class="toc-number">7.2.</span> <span class="toc-text">压缩方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%94%AF%E6%8C%81%E8%AF%AD%E8%A8%80"><span class="toc-number">7.3.</span> <span class="toc-text">支持语言</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E7%AC%A6%E9%9B%86"><span class="toc-number">7.4.</span> <span class="toc-text">字符集</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%BC%A0%E8%BE%93%E5%AE%9A%E9%95%BF%E5%92%8C%E4%B8%8D%E5%AE%9A%E9%95%BF%E7%9A%84%E6%95%B0%E6%8D%AE"><span class="toc-number">8.</span> <span class="toc-text">http传输定长和不定长的数据</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E9%95%BF%E5%8C%85%E4%BD%93"><span class="toc-number">8.1.</span> <span class="toc-text">定长包体</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%8D%E5%AE%9A%E9%95%BF%E5%8C%85%E4%BD%93"><span class="toc-number">8.2.</span> <span class="toc-text">不定长包体</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%B8%AD%E5%A4%84%E7%90%86%E5%A4%A7%E6%96%87%E4%BB%B6%E7%9A%84%E4%BC%A0%E8%BE%93"><span class="toc-number">9.</span> <span class="toc-text">http中处理大文件的传输</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E6%94%AF%E6%8C%81"><span class="toc-number">9.1.</span> <span class="toc-text">如何支持</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Range%E5%AD%97%E6%AE%B5%E6%8B%86%E8%A7%A3"><span class="toc-number">9.2.</span> <span class="toc-text">Range字段拆解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E6%AE%B5%E6%95%B0%E6%8D%AE"><span class="toc-number">9.3.</span> <span class="toc-text">单段数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E6%AE%B5%E6%95%B0%E6%8D%AE"><span class="toc-number">9.4.</span> <span class="toc-text">多段数据</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%B8%AD%E5%A4%84%E7%90%86%E8%A1%A8%E5%8D%95%E6%95%B0%E6%8D%AE%E7%9A%84%E6%8F%90%E4%BA%A4"><span class="toc-number">10.</span> <span class="toc-text">http中处理表单数据的提交</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#application-x-www-form-urlencoded"><span class="toc-number">10.1.</span> <span class="toc-text">application&#x2F;x-www-form-urlencoded</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#multipart-form-data"><span class="toc-number">10.2.</span> <span class="toc-text">multipart&#x2F;form-data</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E7%BB%93"><span class="toc-number">10.3.</span> <span class="toc-text">小结</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E9%98%9F%E5%A4%B4%E9%98%BB%E5%A1%9E"><span class="toc-number">11.</span> <span class="toc-text">解决队头阻塞</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B9%B6%E5%8F%91%E8%BF%9E%E6%8E%A5"><span class="toc-number">11.1.</span> <span class="toc-text">并发连接</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9F%9F%E5%90%8D%E5%88%86%E7%89%87"><span class="toc-number">11.2.</span> <span class="toc-text">域名分片</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%BB%A3%E7%90%86"><span class="toc-number">12.</span> <span class="toc-text">http代理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%9B%B8%E5%85%B3%E5%A4%B4%E9%83%A8%E5%AD%97%E6%AE%B5"><span class="toc-number">12.1.</span> <span class="toc-text">相关头部字段</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Via"><span class="toc-number">12.1.1.</span> <span class="toc-text">Via</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#X-Forwarded-For"><span class="toc-number">12.1.2.</span> <span class="toc-text">X-Forwarded-For</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#X-Real-IP"><span class="toc-number">12.1.3.</span> <span class="toc-text">X-Real-IP</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#X-Forwarded-For%E4%BA%A7%E7%94%9F%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-number">12.2.</span> <span class="toc-text">X-Forwarded-For产生的问题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%B8%AD%E7%9A%84%E4%BB%A3%E7%90%86%E7%BC%93%E5%AD%98"><span class="toc-number">13.</span> <span class="toc-text">http中的代理缓存</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BA%A7%E7%94%9F%E4%BB%A3%E7%90%86%E7%BC%93%E5%AD%98%EF%BC%9F"><span class="toc-number">13.1.</span> <span class="toc-text">为什么产生代理缓存？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%BA%90%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%8E%A7%E5%88%B6"><span class="toc-number">13.2.</span> <span class="toc-text">源服务器的缓存控制</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#private-%E5%92%8C-public"><span class="toc-number">13.2.1.</span> <span class="toc-text">private 和 public</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#proxy-revalidate"><span class="toc-number">13.2.2.</span> <span class="toc-text">proxy-revalidate</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#s-maxage"><span class="toc-number">13.2.3.</span> <span class="toc-text">s-maxage</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E7%BC%93%E5%AD%98%E6%8E%A7%E5%88%B6"><span class="toc-number">13.3.</span> <span class="toc-text">客户端的缓存控制</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#max-stale-%E5%92%8C-min-fresh"><span class="toc-number">13.3.1.</span> <span class="toc-text">max-stale 和 min-fresh</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#only-if-cached"><span class="toc-number">13.3.2.</span> <span class="toc-text">only-if-cached</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%B7%A8%E5%9F%9F%E7%9B%B8%E5%85%B3"><span class="toc-number">14.</span> <span class="toc-text">跨域相关</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E8%B7%A8%E5%9F%9F"><span class="toc-number">14.1.</span> <span class="toc-text">什么是跨域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#CORS"><span class="toc-number">14.2.</span> <span class="toc-text">CORS</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%AE%80%E5%8D%95%E8%AF%B7%E6%B1%82"><span class="toc-number">14.2.1.</span> <span class="toc-text">简单请求</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%9D%9E%E7%AE%80%E5%8D%95%E8%AF%B7%E6%B1%82"><span class="toc-number">14.2.2.</span> <span class="toc-text">非简单请求</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JSONP"><span class="toc-number">14.3.</span> <span class="toc-text">JSONP</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Nginx"><span class="toc-number">14.4.</span> <span class="toc-text">Nginx</span></a></li></ol></li></ol>
    </div>
  </span>
</div>

    
    <div class="content index py4">
        
        <article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    
    <h1 class="posttitle" itemprop="name headline">
        http总结
    </h1>



    <div class="meta">
      <span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
        <span itemprop="name">柯里的语法糖</span>
      </span>
      
    <div class="postdate">
      
        <time datetime="2022-03-05T12:31:10.772Z" itemprop="datePublished">2022-03-05</time>
        
      
    </div>


      
    <div class="article-category">
        <i class="fas fa-archive"></i>
        <a class="category-link" href="/categories/%E7%BD%91%E7%BB%9C/">网络</a>
    </div>


      
    <div class="article-tag">
        <i class="fas fa-tag"></i>
        <a class="tag-link-link" href="/tags/http/" rel="tag">http</a>
    </div>


    </div>
  </header>
  

  <div class="content" itemprop="articleBody">
    <p>本篇文章转载自<a target="_blank" rel="noopener" href="https://juejin.cn/post/6844904100035821575">https://juejin.cn/post/6844904100035821575</a>，这里做了简单梳理，仅作个人笔记使用。</p>
<h2 id="http报文结构"><a href="#http报文结构" class="headerlink" title="http报文结构"></a>http报文结构</h2><p>http的报文结构是<code>header+body</code>的结构，具体：</p>
<p><code>起始行 + 头部 + 空行 + 实体</code></p>
<h3 id="起始行"><a href="#起始行" class="headerlink" title="起始行"></a>起始行</h3><p>对<strong>请求报文</strong>来说，起始行类似于这样:</p>
<p><code>GET /home HTTP/1.1</code></p>
<p>也就是<strong>方法 + 路径 + http版本</strong></p>
<p>对于<strong>响应报文</strong>来说，起始行类似于这样:</p>
<p><code>HTTP/1.1 200 OK</code></p>
<p>响应报文的起始行也叫做<strong>状态行</strong> 。由<strong>http版本、状态码和原因</strong>三部分组成。</p>
<blockquote>
<p>值得注意的是，在起始行中，每两个部分之间用空格隔开，最后一个部分后面应该接一个换行，严格遵循ABNF语法规范。</p>
</blockquote>
<h3 id="头部"><a href="#头部" class="headerlink" title="头部"></a>头部</h3><p>头部字段的格式：</p>
<ol>
<li>字段名不区分大小写</li>
<li>字段名不允许出现空格，不可以出现下划线<code>_</code></li>
<li>字段名后面必须紧接着<code>:</code></li>
</ol>
<h3 id="空行"><a href="#空行" class="headerlink" title="空行"></a>空行</h3><p>空行用来区分开<strong>头部</strong>和<strong>实体</strong></p>
<p>问: 如果说在头部中间故意加一个空行会怎么样？</p>
<p>那么空行后的内容全部被视为实体。</p>
<h3 id="实体"><a href="#实体" class="headerlink" title="实体"></a>实体</h3><p><code>body</code> 部分。请求报文对应请求体, 响应报文对应响应体。</p>
<h2 id="http请求方法"><a href="#http请求方法" class="headerlink" title="http请求方法"></a>http请求方法</h2><p>http/1.1规定了以下请求方法(注意，都是大写):</p>
<ul>
<li>GET: 通常用来获取资源</li>
<li>HEAD: 获取资源的元信息</li>
<li>POST: 提交数据，即上传数据</li>
<li>PUT: 修改数据</li>
<li>DELETE: 删除资源(几乎用不到)</li>
<li>CONNECT: 建立连接隧道，用于代理服务器</li>
<li>OPTIONS: 列出可对资源实行的请求方法，用来跨域请求</li>
<li>TRACE: 追踪请求-响应的传输路径</li>
</ul>
<h3 id="GET和POST的区别"><a href="#GET和POST的区别" class="headerlink" title="GET和POST的区别"></a>GET和POST的区别</h3><ul>
<li>从<strong>缓存</strong>的角度，GET 请求会被浏览器主动缓存下来，留下历史记录，而 POST 默认不会。</li>
<li>从<strong>编码</strong>的角度，GET 只能进行 URL 编码，只能接收 ASCII 字符，而 POST 没有限制。</li>
<li>从<strong>参数</strong>的角度，GET 一般放在 URL 中，因此不安全，POST 放在请求体中，更适合传输敏感信息。</li>
<li>从<strong>幂等性</strong>的角度，GET是幂等的，而POST不是。(幂等表示执行相同的操作，结果也是相同的)</li>
<li>从<strong>TCP</strong>的角度，GET 请求会把请求报文一次性发出去，而 POST 会分为两个 TCP 数据包，首先发 header 部分，如果服务器响应 100(continue)， 然后发 body 部分。(火狐浏览器除外，它的 POST 请求只发一个 TCP 包)</li>
</ul>
<h2 id="URI"><a href="#URI" class="headerlink" title="URI"></a>URI</h2><p><strong>URI</strong> , 全称为(Uniform Resource Identifier), 也就是统一资源标识符，它的作用很简单，就是区分互联网上不同的资源。</p>
<h3 id="URI结构"><a href="#URI结构" class="headerlink" title="URI结构"></a>URI结构</h3><p>URI 真正最完整的结构是这样的。</p>
<p><img src="https://codertzm.oss-cn-chengdu.aliyuncs.com/image-20220228112828121.png" alt="image-20220228112828121"></p>
<p><strong>scheme</strong> 表示协议名，比如<code>http</code>, <code>https</code>, <code>file</code>等等。后面必须和<code>://</code>连在一起。</p>
<p><strong>user:passwd</strong>@ 表示登录主机时的用户信息，不过很不安全，不推荐使用，也不常用。</p>
<p><strong>host:port</strong>表示主机名和端口。</p>
<p><strong>path</strong>表示请求路径，标记资源所在位置。</p>
<p><strong>query</strong>表示查询参数，为<code>key=val</code>这种形式，多个键值对之间用<code>&amp;</code>隔开。</p>
<p><strong>fragment</strong>表示 URI 所定位的资源内的一个<strong>锚点</strong>，浏览器可以根据这个锚点跳转到对应的位置。</p>
<p>举个例子:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://www.baidu.com/s?wd=HTTP&amp;rsv_spt=1</span><br></pre></td></tr></table></figure>

<p>这个 URI 中，<code>https</code>即<code>scheme</code>部分，<code>www.baidu.com</code>为<code>host:port</code>部分（注意，http 和 https 的默认端口分别为80、443），<code>/s</code>为<code>path</code>部分，而<code>wd=HTTP&amp;rsv_spt=1</code>就是<code>query</code>部分。</p>
<h3 id="URI-编码"><a href="#URI-编码" class="headerlink" title="URI 编码"></a>URI 编码</h3><p>URI 只能使用<code>ASCII</code>, ASCII 之外的字符是不支持显示的，而且还有一部分符号是界定符，如果不加以处理就会导致解析出错。</p>
<p>因此，URI 引入了<code>编码</code>机制，将所有<strong>非 ASCII 码字符</strong>和<strong>界定符</strong>转为十六进制字节值，然后在前面加个<code>%</code>。</p>
<p>如，空格被转义成了<code>%20</code>，<strong>三元</strong>被转义成了<code>%E4%B8%89%E5%85%83</code>。</p>
<h2 id="http状态码"><a href="#http状态码" class="headerlink" title="http状态码"></a>http状态码</h2><p>RFC 规定 HTTP 的状态码为<strong>三位数</strong>，被分为五类:</p>
<ul>
<li><strong>1xx</strong>: 表示目前是协议处理的中间状态，还需要后续操作。</li>
<li><strong>2xx</strong>: 表示成功状态。</li>
<li><strong>3xx</strong>: 重定向状态，资源位置发生变动，需要重新请求。</li>
<li><strong>4xx</strong>: 请求报文有误。</li>
<li><strong>5xx</strong>: 服务器端发生错误。</li>
</ul>
<h3 id="1xx"><a href="#1xx" class="headerlink" title="1xx"></a>1xx</h3><p><strong>101 Switching Protocols</strong>。在<code>HTTP</code>升级为<code>WebSocket</code>的时候，如果服务器同意变更，就会发送状态码 101。</p>
<h3 id="2xx"><a href="#2xx" class="headerlink" title="2xx"></a>2xx</h3><p><strong>200 OK</strong>是见得最多的成功状态码。通常在响应体中放有数据。</p>
<p><strong>204 No Content</strong>含义与 200 相同，但响应头后没有 body 数据。</p>
<p><strong>206 Partial Content</strong>顾名思义，表示部分内容，它的使用场景为 HTTP 分块下载和断点续传，当然也会带上相应的响应头字段<code>Content-Range</code>。</p>
<h3 id="3xx"><a href="#3xx" class="headerlink" title="3xx"></a>3xx</h3><p><strong>301 Moved Permanently</strong>即永久重定向，对应着<strong>302 Found</strong>，即临时重定向。</p>
<p>比如你的网站从 HTTP 升级到了 HTTPS 了，以前的站点再也不用了，应当返回<code>301</code>，这个时候浏览器默认会做缓存优化，在第二次访问的时候自动访问重定向的那个地址。</p>
<p>而如果只是暂时不可用，那么直接返回<code>302</code>即可，和<code>301</code>不同的是，浏览器并不会做缓存优化。</p>
<p><strong>304 Not Modified</strong>: 当协商缓存命中时会返回这个状态码。</p>
<h3 id="4xx"><a href="#4xx" class="headerlink" title="4xx"></a>4xx</h3><p><strong>400 Bad Request</strong>: 开发者经常看到一头雾水，只是笼统地提示了一下错误，并不知道哪里出错了。</p>
<p><strong>403 Forbidden</strong>: 这实际上并不是请求报文出错，而是服务器禁止访问，原因有很多，比如法律禁止、信息敏感。</p>
<p><strong>404 Not Found</strong>: 资源未找到，表示没在服务器上找到相应的资源。</p>
<p><strong>405 Method Not Allowed</strong>: 请求方法不被服务器端允许。</p>
<p><strong>406 Not Acceptable</strong>: 资源无法满足客户端的条件。</p>
<p><strong>408 Request Timeout</strong>: 服务器等待了太长时间。</p>
<p><strong>409 Conflict</strong>: 多个请求发生了冲突。</p>
<p><strong>413 Request Entity Too Large</strong>: 请求体的数据过大。</p>
<p><strong>414 Request-URI Too Long</strong>: 请求行里的 URI 太大。</p>
<p><strong>429 Too Many Request</strong>: 客户端发送的请求过多。</p>
<p><strong>431 Request Header Fields Too Large</strong>请求头的字段内容太大。</p>
<h3 id="5xx"><a href="#5xx" class="headerlink" title="5xx"></a>5xx</h3><p><strong>500 Internal Server Error</strong>: 仅仅告诉你服务器出错了，出了啥错咱也不知道。</p>
<p><strong>501 Not Implemented</strong>: 表示客户端请求的功能还不支持。</p>
<p><strong>502 Bad Gateway</strong>: 服务器自身是正常的，但访问的时候出错了，啥错误咱也不知道。</p>
<p><strong>503 Service Unavailable</strong>: 表示服务器当前很忙，暂时无法响应服务。</p>
<h2 id="http的特点"><a href="#http的特点" class="headerlink" title="http的特点"></a>http的特点</h2><ol>
<li><p><strong>灵活可扩展</strong>，主要体现在两个方面。一个是语义上的自由，只规定了基本格式，比如空格分隔单词，换行分隔字段，其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性，不仅仅可以传输文本，还能传输图片、视频等任意数据，非常方便。</p>
</li>
<li><p><strong>可靠传输</strong>。HTTP 基于 TCP/IP，因此把这一特性继承了下来。这属于 TCP 的特性，不具体介绍了。</p>
</li>
<li><p><strong>请求-应答</strong>。也就是<code>一发一收</code>、<code>有来有回</code>， 当然这个请求方和应答方不单单指客户端和服务器之间，如果某台服务器作为代理来连接后端的服务端，那么这台服务器也会扮演<strong>请求方</strong>的角色。</p>
</li>
<li><p><strong>无状态</strong>。这里的状态是指<strong>通信过程的上下文信息</strong>，而每次 http 请求都是独立、无关的，默认不需要保留状态信息。</p>
</li>
</ol>
<h2 id="http的缺点"><a href="#http的缺点" class="headerlink" title="http的缺点"></a>http的缺点</h2><h3 id="无状态"><a href="#无状态" class="headerlink" title="无状态"></a>无状态</h3><p>所谓的优点和缺点还是要分场景来看的，对于 HTTP 而言，最具争议的地方在于它的<strong>无状态</strong>。</p>
<p>在需要长连接的场景中，需要保存大量的上下文信息，以免传输大量重复的信息，那么这时候无状态就是 http 的缺点了。</p>
<p>但与此同时，另外一些应用仅仅只是为了获取一些数据，不需要保存连接上下文信息，无状态反而减少了网络开销，成为了 http 的优点。</p>
<h3 id="明文传输"><a href="#明文传输" class="headerlink" title="明文传输"></a>明文传输</h3><p>即协议里的报文(主要指的是头部)不使用二进制数据，而是文本形式。</p>
<p>这当然对于调试提供了便利，但同时也让 HTTP 的报文信息暴露给了外界，给攻击者也提供了便利。<code>WIFI陷阱</code>就是利用 HTTP 明文传输的缺点，诱导你连上热点，然后疯狂抓你所有的流量，从而拿到你的敏感信息。</p>
<h3 id="队头阻塞"><a href="#队头阻塞" class="headerlink" title="队头阻塞"></a>队头阻塞</h3><p>当 http 开启长连接时，共用一个 TCP 连接，同一时刻只能处理一个请求，那么当前请求耗时过长的情况下，其它的请求只能处于阻塞状态，也就是著名的<strong>队头阻塞</strong>问题。</p>
<blockquote>
<p>HTTP/1.1 默认使用了持久连接，多个请求可以复用同一个 TCP 连接，但是在同一个 TCP 连接里面，数据请求的通信次序<br>是固定的。服务器只有处理完一个请求的响应后，才会进行下一个请求的处理，如果前面请求的响应特别慢的话，就会造成许<br>多请求排队等待的情况，这种情况被称为“队头阻塞”。队头阻塞会导致持久连接在达到最大数量时，剩余的资源需要等待其他<br>资源请求完成后才能发起请求。</p>
</blockquote>
<h2 id="Accept系列字段"><a href="#Accept系列字段" class="headerlink" title="Accept系列字段"></a>Accept系列字段</h2><p>对于<code>Accept</code>系列字段的介绍分为四个部分: <strong>数据格式</strong>、<strong>压缩方式</strong>、<strong>支持语言</strong>和<strong>字符集</strong>。</p>
<h3 id="数据格式"><a href="#数据格式" class="headerlink" title="数据格式"></a>数据格式</h3><p>HTTP 灵活的特性，它支持非常多的数据格式，那么这么多格式的数据一起到达客户端，客户端怎么知道它的格式呢？</p>
<p>HTTP 从<strong>MIME type</strong>取了一部分来标记报文 body 部分的数据类型，这些类型体现在<code>Content-Type</code>这个字段，当然这是针对于发送端而言，接收端想要收到特定类型的数据，也可以用<code>Accept</code>字段。</p>
<blockquote>
<p><strong>MIME</strong>(Multipurpose Internet Mail Extensions, <strong>多用途互联网邮件扩展</strong>)。它首先用在电子邮件系统中，让邮件可以发任意类型的数据，这对于 HTTP 来说也是通用的。</p>
</blockquote>
<p>具体而言，这两个字段的取值可以分为下面几类:</p>
<ul>
<li>text： text/html, text/plain, text/css 等</li>
<li>image: image/gif, image/jpeg, image/png 等</li>
<li>audio/video: audio/mpeg, video/mp4 等</li>
<li>application: application/json, application/javascript, application/pdf, application/octet-stream</li>
</ul>
<h3 id="压缩方式"><a href="#压缩方式" class="headerlink" title="压缩方式"></a>压缩方式</h3><p>一般这些数据都是会进行编码压缩的，采取什么样的压缩方式就体现在了发送方的<code>Content-Encoding</code>字段上， 同样的，接收什么样的压缩方式体现在了接受方的<code>Accept-Encoding</code>字段上。这个字段的取值有下面几种：</p>
<ul>
<li>gzip: 当今最流行的压缩格式</li>
<li>deflate: 另外一种著名的压缩格式</li>
<li>br: 一种专门为 HTTP 发明的压缩算法</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 发送端</span><br><span class="line">Content-Encoding: gzip</span><br><span class="line">// 接收端</span><br><span class="line">Accept-Encoding: gzip</span><br></pre></td></tr></table></figure>

<h3 id="支持语言"><a href="#支持语言" class="headerlink" title="支持语言"></a>支持语言</h3><p>对于发送方而言，还有一个<code>Content-Language</code>字段，在需要实现国际化的方案当中，可以用来指定支持的语言，在接受方对应的字段为<code>Accept-Language</code>。如:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 发送端</span><br><span class="line">Content-Language: zh-CN, zh, en</span><br><span class="line">// 接收端</span><br><span class="line">Accept-Language: zh-CN, zh, en</span><br></pre></td></tr></table></figure>

<h3 id="字符集"><a href="#字符集" class="headerlink" title="字符集"></a>字符集</h3><p>最后是一个比较特殊的字段, 在接收端对应为<code>Accept-Charset</code>，指定可以接受的字符集，而在发送端并没有对应的<code>Content-Charset</code>, 而是直接放在了<code>Content-Type</code>中，以<strong>charset</strong>属性指定。如:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 发送端</span><br><span class="line">Content-Type: text/html; charset=utf-8</span><br><span class="line">// 接收端</span><br><span class="line">Accept-Charset: charset=utf-8</span><br></pre></td></tr></table></figure>

<h2 id="http传输定长和不定长的数据"><a href="#http传输定长和不定长的数据" class="headerlink" title="http传输定长和不定长的数据"></a>http传输定长和不定长的数据</h2><h3 id="定长包体"><a href="#定长包体" class="headerlink" title="定长包体"></a>定长包体</h3><p>对于定长包体而言，发送端在传输的时候一般会带上 <code>Content-Length</code>, 来指明包体的长度。</p>
<p><code>Content-Length</code>对于 http 传输过程起到了十分关键的作用，如果设置不当可以直接导致传输失败。</p>
<h3 id="不定长包体"><a href="#不定长包体" class="headerlink" title="不定长包体"></a>不定长包体</h3><p>对于<code>不定长包体</code>而言是如何传输的呢？</p>
<p>这里就必须介绍另外一个 http 头部字段了:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Transfer-Encoding: chunked</span><br></pre></td></tr></table></figure>

<p>表示分块传输数据，设置这个字段后会自动产生两个效果:</p>
<ul>
<li>Content-Length 字段会被忽略</li>
<li>基于长连接持续推送动态内容</li>
</ul>
<p>我们依然以一个实际的例子来模拟分块传输，nodejs 程序如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">const http = require(&#x27;http&#x27;);</span><br><span class="line"></span><br><span class="line">const server = http.createServer();</span><br><span class="line"></span><br><span class="line">server.on(&#x27;request&#x27;, (req, res) =&gt; &#123;</span><br><span class="line">  if(req.url === &#x27;/&#x27;) &#123;</span><br><span class="line">    res.setHeader(&#x27;Content-Type&#x27;, &#x27;text/html; charset=utf8&#x27;);</span><br><span class="line">    res.setHeader(&#x27;Content-Length&#x27;, 10);</span><br><span class="line">    res.setHeader(&#x27;Transfer-Encoding&#x27;, &#x27;chunked&#x27;);</span><br><span class="line">    res.write(&quot;&lt;p&gt;来啦&lt;/p&gt;&quot;);</span><br><span class="line">    setTimeout(() =&gt; &#123;</span><br><span class="line">      res.write(&quot;第一次传输&lt;br/&gt;&quot;);</span><br><span class="line">    &#125;, 1000);</span><br><span class="line">    setTimeout(() =&gt; &#123;</span><br><span class="line">      res.write(&quot;第二次传输&quot;);</span><br><span class="line">      res.end()</span><br><span class="line">    &#125;, 2000);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">server.listen(8009, () =&gt; &#123;</span><br><span class="line">  console.log(&quot;成功启动&quot;);</span><br><span class="line">&#125;)</span><br><span class="line">复制代码</span><br></pre></td></tr></table></figure>

<p>访问效果入下:</p>
<p><img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd728ba3840d~tplv-t2oaga2asx-watermark.awebp" alt="img"></p>
<p>用 telnet 抓到的响应如下:</p>
<p><img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd78332368a0~tplv-t2oaga2asx-watermark.awebp" alt="img"></p>
<p>注意，<code>Connection: keep-alive</code>及之前的为响应行和响应头，后面的内容为响应体，这两部分用换行符隔开。</p>
<p>响应体的结构比较有意思，如下所示:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">chunk长度(16进制的数)</span><br><span class="line">第一个chunk的内容</span><br><span class="line">chunk长度(16进制的数)</span><br><span class="line">第二个chunk的内容</span><br><span class="line">......</span><br><span class="line">0</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>最后是留有有一个<code>空行</code>的，这一点请大家注意。</p>
<p>以上便是 http 对于<strong>定长数据</strong>和<strong>不定长数据</strong>的传输方式。</p>
<h2 id="http中处理大文件的传输"><a href="#http中处理大文件的传输" class="headerlink" title="http中处理大文件的传输"></a>http中处理大文件的传输</h2><p>对于几百 M 甚至上 G 的大文件来说，如果要一口气全部传输过来显然是不现实的，会有大量的等待时间，严重影响用户体验。因此，HTTP 针对这一场景，采取了<code>范围请求</code>的解决方案，允许客户端仅仅请求一个资源的一部分。</p>
<h3 id="如何支持"><a href="#如何支持" class="headerlink" title="如何支持"></a>如何支持</h3><p>当然，前提是服务器要支持<strong>范围请求</strong>，要支持这个功能，就必须加上这样一个响应头:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Accept-Ranges: none</span><br></pre></td></tr></table></figure>

<p>用来告知客户端这边是支持范围请求的。</p>
<h3 id="Range字段拆解"><a href="#Range字段拆解" class="headerlink" title="Range字段拆解"></a>Range字段拆解</h3><p>而对于客户端而言，它需要指定请求哪一部分，通过<code>Range</code>这个请求头字段确定，格式为<code>bytes=x-y</code>。接下来就来讨论一下这个 Range 的书写格式:</p>
<ul>
<li><strong>0-499</strong>表示从开始到第 499 个字节。</li>
<li><strong>500</strong>- 表示从第 500 字节到文件终点。</li>
<li><strong>-100</strong>表示文件的最后100个字节。</li>
</ul>
<p>服务器收到请求之后，首先验证范围<strong>是否合法</strong>，如果越界了那么返回<code>416</code>错误码，否则读取相应片段，返回<code>206</code>状态码。</p>
<p>同时，服务器需要添加<code>Content-Range</code>字段，这个字段的格式根据请求头中<code>Range</code>字段的不同而有所差异。</p>
<p>具体来说，请求<code>单段数据</code>和请求<code>多段数据</code>，响应头是不一样的。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 单段数据</span><br><span class="line">Range: bytes=0-9</span><br><span class="line">// 多段数据</span><br><span class="line">Range: bytes=0-9, 30-39</span><br></pre></td></tr></table></figure>

<h3 id="单段数据"><a href="#单段数据" class="headerlink" title="单段数据"></a>单段数据</h3><p>对于<code>单段数据</code>的请求，返回的响应如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 206 Partial Content</span><br><span class="line">Content-Length: 10</span><br><span class="line">Accept-Ranges: bytes</span><br><span class="line">Content-Range: bytes 0-9/100</span><br><span class="line"></span><br><span class="line">i am xxxxx</span><br></pre></td></tr></table></figure>

<p>值得注意的是<code>Content-Range</code>字段，<code>0-9</code>表示请求的返回，<code>100</code>表示资源的总大小，很好理解。</p>
<h3 id="多段数据"><a href="#多段数据" class="headerlink" title="多段数据"></a>多段数据</h3><p>接下来我们看看多段请求的情况。得到的响应会是下面这个形式:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 206 Partial Content</span><br><span class="line">Content-Type: multipart/byteranges; boundary=00000010101</span><br><span class="line">Content-Length: 189</span><br><span class="line">Connection: keep-alive</span><br><span class="line">Accept-Ranges: bytes</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">--00000010101</span><br><span class="line">Content-Type: text/plain</span><br><span class="line">Content-Range: bytes 0-9/96</span><br><span class="line"></span><br><span class="line">i am xxxxx</span><br><span class="line">--00000010101</span><br><span class="line">Content-Type: text/plain</span><br><span class="line">Content-Range: bytes 20-29/96</span><br><span class="line"></span><br><span class="line">eex jspy e</span><br><span class="line">--00000010101--</span><br></pre></td></tr></table></figure>

<p>这个时候出现了一个非常关键的字段<code>Content-Type: multipart/byteranges;boundary=00000010101</code>，它代表了信息量是这样的:</p>
<ul>
<li>请求一定是多段数据请求</li>
<li>响应体中的分隔符是 00000010101</li>
</ul>
<p>因此，在响应体中各段数据之间会由这里指定的分隔符分开，而且在最后的分隔末尾添上<code>--</code>表示结束。</p>
<h2 id="http中处理表单数据的提交"><a href="#http中处理表单数据的提交" class="headerlink" title="http中处理表单数据的提交"></a>http中处理表单数据的提交</h2><p>在 http 中，有两种主要的表单提交的方式，体现在两种不同的<code>Content-Type</code>取值:</p>
<ul>
<li>application/x-www-form-urlencoded</li>
<li>multipart/form-data</li>
</ul>
<p>由于表单提交一般是<code>POST</code>请求，很少考虑<code>GET</code>，因此这里我们将默认提交的数据放在请求体中。</p>
<h3 id="application-x-www-form-urlencoded"><a href="#application-x-www-form-urlencoded" class="headerlink" title="application/x-www-form-urlencoded"></a>application/x-www-form-urlencoded</h3><p>对于<code>application/x-www-form-urlencoded</code>格式的表单内容，有以下特点:</p>
<ul>
<li>其中的数据会被编码成以<code>&amp;</code>分隔的键值对</li>
<li>字符以<strong>URL编码方式</strong>编码。</li>
</ul>
<p>如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">// 转换过程: &#123;a: 1, b: 2&#125; -&gt; a=1&amp;b=2 -&gt; 如下(最终形式)</span><br><span class="line">&quot;a%3D1%26b%3D2&quot;</span><br></pre></td></tr></table></figure>

<h3 id="multipart-form-data"><a href="#multipart-form-data" class="headerlink" title="multipart/form-data"></a>multipart/form-data</h3><p>对于<code>multipart/form-data</code>而言:</p>
<ul>
<li>请求头中的<code>Content-Type</code>字段会包含<code>boundary</code>，且<code>boundary</code>的值有浏览器默认指定。例: <code>Content-Type: multipart/form-data;boundary=----WebkitFormBoundaryRRJKeWfHPGrS4LKe</code>。</li>
<li>数据会分为多个部分，每两个部分之间通过分隔符来分隔，每部分表述均有 HTTP 头部描述子包体，如<code>Content-Type</code>，在最后的分隔符会加上<code>--</code>表示结束。</li>
</ul>
<p>相应的<code>请求体</code>是下面这样:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Content-Disposition: form-data;name=&quot;data1&quot;;</span><br><span class="line">Content-Type: text/plain</span><br><span class="line">data1</span><br><span class="line">----WebkitFormBoundaryRRJKeWfHPGrS4LKe</span><br><span class="line">Content-Disposition: form-data;name=&quot;data2&quot;;</span><br><span class="line">Content-Type: text/plain</span><br><span class="line">data2</span><br><span class="line">----WebkitFormBoundaryRRJKeWfHPGrS4LKe--</span><br></pre></td></tr></table></figure>

<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>值得一提的是，<code>multipart/form-data</code> 格式最大的特点在于:<strong>每一个表单元素都是独立的资源表述</strong>。另外，你可能在写业务的过程中，并没有注意到其中还有<code>boundary</code>的存在，如果你打开抓包工具，确实可以看到不同的表单元素被拆分开了，之所以在平时感觉不到，是以为浏览器和 HTTP 给你封装了这一系列操作。</p>
<p>而且，在实际的场景中，对于图片等文件的上传，基本采用<code>multipart/form-data</code>而不用<code>application/x-www-form-urlencoded</code>，因为没有必要做 URL 编码，带来巨大耗时的同时也占用了更多的空间。</p>
<h2 id="解决队头阻塞"><a href="#解决队头阻塞" class="headerlink" title="解决队头阻塞"></a>解决队头阻塞</h2><h3 id="并发连接"><a href="#并发连接" class="headerlink" title="并发连接"></a>并发连接</h3><p>对于一个域名允许分配多个长连接，那么相当于增加了任务队列，不至于一个队伍的任务阻塞其它所有任务。在RFC2616规定过客户端最多并发 2 个连接，不过事实上在现在的浏览器标准中，这个上限要多很多，Chrome 中是 6 个。</p>
<p>但其实，即使是提高了并发连接，还是不能满足人们对性能的需求。</p>
<h3 id="域名分片"><a href="#域名分片" class="headerlink" title="域名分片"></a>域名分片</h3><p>一个域名不是可以并发 6 个长连接吗？那我就多分几个域名。</p>
<p>比如 content1.sanyuan.com 、content2.sanyuan.com。</p>
<p>这样一个<code>sanyuan.com</code>域名下可以分出非常多的二级域名，而它们都指向同样的一台服务器，能够并发的长连接数更多了，事实上也更好地解决了队头阻塞的问题。</p>
<h2 id="http代理"><a href="#http代理" class="headerlink" title="http代理"></a>http代理</h2><p>引入代理之后，作为代理的服务器相当于一个中间人的角色，对于客户端而言，表现为服务器进行响应；而对于源服务器，表现为客户端发起请求，具有<strong>双重身份</strong>。</p>
<p>代理服务器的功能：</p>
<ol>
<li><strong>负载均衡</strong>，代理服务器可以拿到这个请求之后，可以通过特定的算法分发给不同的源服务器，让各台源服务器的负载尽量平均。</li>
<li><strong>保障安全</strong>，利用<strong>心跳</strong>机制监控后台的服务器，一旦发现故障机就将其踢出集群。并且对于上下行的数据进行过滤，对非法 IP 限流，这些都是代理服务器的工作。</li>
<li><strong>缓存代理</strong>，将内容缓存到代理服务器，使得客户端可以直接从代理服务器获得而不用到源服务器那里。</li>
</ol>
<h3 id="相关头部字段"><a href="#相关头部字段" class="headerlink" title="相关头部字段"></a>相关头部字段</h3><h4 id="Via"><a href="#Via" class="headerlink" title="Via"></a>Via</h4><p>代理服务器需要标明自己的身份，在 HTTP 传输中留下自己的痕迹，怎么办呢？</p>
<p>通过<code>Via</code>字段来记录。举个例子，现在中间有两台代理服务器，在客户端发送请求后会经历这样一个过程:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">客户端 -&gt; 代理1 -&gt; 代理2 -&gt; 源服务器</span><br></pre></td></tr></table></figure>

<p>在源服务器收到请求后，会在<code>请求头</code>拿到这个字段:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Via: proxy_server1, proxy_server2</span><br></pre></td></tr></table></figure>

<p>而源服务器响应时，最终在客户端会拿到这样的<code>响应头</code>:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Via: proxy_server2, proxy_server1</span><br></pre></td></tr></table></figure>

<p>可以看到，<code>Via</code>中代理的顺序即为在 HTTP 传输中报文传达的顺序。</p>
<h4 id="X-Forwarded-For"><a href="#X-Forwarded-For" class="headerlink" title="X-Forwarded-For"></a>X-Forwarded-For</h4><p>字面意思就是<code>为谁转发</code>, 它记录的是<strong>请求方</strong>的<code>IP</code>地址(注意，和<code>Via</code>区分开，<code>X-Forwarded-For</code>记录的是请求方这一个IP)。</p>
<h4 id="X-Real-IP"><a href="#X-Real-IP" class="headerlink" title="X-Real-IP"></a>X-Real-IP</h4><p>是一种获取用户真实 IP 的字段，不管中间经过多少代理，这个字段始终记录最初的客户端的IP。</p>
<p>相应的，还有<code>X-Forwarded-Host</code>和<code>X-Forwarded-Proto</code>，分别记录<strong>客户端</strong>(注意哦，不包括代理)的<code>域名</code>和<code>协议名</code>。</p>
<h3 id="X-Forwarded-For产生的问题"><a href="#X-Forwarded-For产生的问题" class="headerlink" title="X-Forwarded-For产生的问题"></a>X-Forwarded-For产生的问题</h3><p>前面可以看到，<code>X-Forwarded-For</code>这个字段记录的是请求方的 IP，这意味着每经过一个不同的代理，这个字段的名字都要变，从<code>客户端</code>到<code>代理1</code>，这个字段是客户端的 IP，从<code>代理1</code>到<code>代理2</code>，这个字段就变为了代理1的 IP。</p>
<p>但是这会产生两个问题:</p>
<ol>
<li>意味着代理必须解析 HTTP 请求头，然后修改，比直接转发数据性能下降。</li>
<li>在 HTTPS 通信加密的过程中，原始报文是不允许修改的。</li>
</ol>
<p>由此产生了<code>代理协议</code>，一般使用明文版本，只需要在 HTTP 请求行上面加上这样格式的文本即可:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// PROXY + TCP4/TCP6 + 请求方地址 + 接收方地址 + 请求端口 + 接收端口</span><br><span class="line">PROXY TCP4 0.0.0.1 0.0.0.2 1111 2222</span><br><span class="line">GET / HTTP/1.1</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<p>这样就可以解决<code>X-Forwarded-For</code>带来的问题了。</p>
<h2 id="http中的代理缓存"><a href="#http中的代理缓存" class="headerlink" title="http中的代理缓存"></a>http中的代理缓存</h2><h3 id="为什么产生代理缓存？"><a href="#为什么产生代理缓存？" class="headerlink" title="为什么产生代理缓存？"></a>为什么产生代理缓存？</h3><p>对于源服务器来说，它也是有缓存的，比如<strong>Redis, Memcache</strong>，但对于 HTTP 缓存来说，如果每次客户端缓存失效都要到源服务器获取，那给源服务器的压力是很大的。</p>
<p>由此引入了<strong>缓存代理</strong>的机制。让<code>代理服务器</code>接管一部分的服务端HTTP缓存，客户端缓存过期后<strong>就近</strong>到代理缓存中获取，代理缓存过期了才请求源服务器，这样流量巨大的时候能明显降低源服务器的压力。</p>
<p>那缓存代理究竟是如何做到的呢？</p>
<p>总的来说，缓存代理的控制分为两部分，一部分是<strong>源服务器</strong>端的控制，一部分是<strong>客户端</strong>的控制。</p>
<h3 id="源服务器的缓存控制"><a href="#源服务器的缓存控制" class="headerlink" title="源服务器的缓存控制"></a>源服务器的缓存控制</h3><h4 id="private-和-public"><a href="#private-和-public" class="headerlink" title="private 和 public"></a>private 和 public</h4><p>在源服务器的响应头中，会加上<code>Cache-Control</code>这个字段进行缓存控制字段，那么它的值当中可以加入<code>private</code>或者<code>public</code>表示是否允许代理服务器缓存，前者禁止，后者为允许。</p>
<p>比如对于一些非常私密的数据，如果缓存到代理服务器，别人直接访问代理就可以拿到这些数据，是非常危险的，因此对于这些数据一般是不会允许代理服务器进行缓存的，将响应头部的<code>Cache-Control</code>设为<code>private</code>，而不是<code>public</code>。</p>
<h4 id="proxy-revalidate"><a href="#proxy-revalidate" class="headerlink" title="proxy-revalidate"></a>proxy-revalidate</h4><p><code>must-revalidate</code>的意思是<strong>客户端</strong>缓存过期就去源服务器获取，而<code>proxy-revalidate</code>则表示<strong>代理服务器</strong>的缓存过期后到源服务器获取。</p>
<h4 id="s-maxage"><a href="#s-maxage" class="headerlink" title="s-maxage"></a>s-maxage</h4><p><code>s</code>是<code>share</code>的意思，限定了缓存在代理服务器中可以存放多久，和限制客户端缓存时间的<code>max-age</code>并不冲突。</p>
<p>讲了这几个字段，我们不妨来举个小例子，源服务器在响应头中加入这样一个字段:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cache-Control: public, max-age=1000, s-maxage=2000</span><br></pre></td></tr></table></figure>

<p>相当于源服务器说: 我这个响应是允许代理服务器缓存的，客户端缓存过期了到代理中拿，并且在客户端的缓存时间为 1000 秒，在代理服务器中的缓存时间为 2000 s。</p>
<h3 id="客户端的缓存控制"><a href="#客户端的缓存控制" class="headerlink" title="客户端的缓存控制"></a>客户端的缓存控制</h3><h4 id="max-stale-和-min-fresh"><a href="#max-stale-和-min-fresh" class="headerlink" title="max-stale 和 min-fresh"></a>max-stale 和 min-fresh</h4><p>在客户端的请求头中，可以加入这两个字段，来对代理服务器上的缓存进行<strong>宽容</strong>和<strong>限制</strong>操作。比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">max-stale: 5</span><br></pre></td></tr></table></figure>

<p>表示客户端到代理服务器上拿缓存的时候，即使代理缓存过期了也不要紧，只要过期时间在<strong>5秒之内</strong>，还是可以从代理中获取的。</p>
<p>又比如:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">min-fresh: 5</span><br></pre></td></tr></table></figure>

<p>表示代理缓存需要一定的新鲜度，不要等到缓存刚好到期再拿，一定要在<strong>到期前 5 秒</strong>之前的时间拿，否则拿不到。</p>
<h4 id="only-if-cached"><a href="#only-if-cached" class="headerlink" title="only-if-cached"></a>only-if-cached</h4><p>这个字段加上后表示客户端只会接受代理缓存，而不会接受源服务器的响应。如果代理缓存无效，则直接返回<code>504（Gateway Timeout）</code>。</p>
<h2 id="跨域相关"><a href="#跨域相关" class="headerlink" title="跨域相关"></a>跨域相关</h2><p>在前后端分离的开发模式中，经常会遇到跨域问题，即 Ajax 请求发出去了，服务器也成功响应了，前端就是拿不到这个响应。接下来我们就来好好讨论一下这个问题。</p>
<h3 id="什么是跨域"><a href="#什么是跨域" class="headerlink" title="什么是跨域"></a>什么是跨域</h3><p>浏览器遵循<strong>同源政策</strong>(<code>scheme(协议)</code>、<code>host(主机)</code>和<code>port(端口)</code>都相同则为<code>同源</code>)。非同源站点有这样一些限制:</p>
<ul>
<li>不能读取和修改对方的 DOM</li>
<li>不读访问对方的 Cookie、IndexDB 和 LocalStorage</li>
<li>限制 XMLHttpRequest 请求。(后面的话题着重围绕这个)</li>
</ul>
<p>当浏览器向目标 URI 发 Ajax 请求时，只要当前 URL 和目标 URL 不同源，则产生跨域，被称为<code>跨域请求</code>。</p>
<p>跨域请求的响应一般会被浏览器所拦截，注意，是被浏览器拦截，响应其实是成功到达客户端了。那这个拦截是如何发生呢？</p>
<p>首先要知道的是，浏览器是多进程的，以 Chrome 为例，进程组成如下：</p>
<p><img src="https://codertzm.oss-cn-chengdu.aliyuncs.com/v2-b61cab529fa31301bde290813b4587fc_1440w.jpg" alt="v2-b61cab529fa31301bde290813b4587fc_1440w"></p>
<p><strong>WebKit 渲染引擎</strong>和<strong>V8 引擎</strong>都在渲染进程当中。</p>
<p>当<code>xhr.send</code>被调用，即 Ajax 请求准备发送的时候，其实还只是在渲染进程的处理。为了防止黑客通过脚本触碰到系统资源，浏览器将每一个渲染进程装进了沙箱，并且为了防止 CPU 芯片一直存在的<strong>Spectre</strong> 和 <strong>Meltdown</strong>漏洞，采取了<code>站点隔离</code>的手段，给每一个不同的站点(一级域名不同)分配了沙箱，互不干扰。具体见<a href="https://link.juejin.cn/?target=https://www.youtube.com/watch?v=dBuykrdhK-A&feature=emb_logo">YouTube上Chromium安全团队的演讲视频</a>。</p>
<p>在沙箱当中的渲染进程是没有办法发送网络请求的，那怎么办？只能通过网络进程来发送。那这样就涉及到进程间通信(IPC，Inter Process Communication)了。接下来我们看看 chromium 当中进程间通信是如何完成的，在 chromium 源码中调用顺序如下:</p>
<p><img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd924eaecb41~tplv-t2oaga2asx-watermark.awebp" alt="img"></p>
<p>可能看了你会比较懵，如果想深入了解可以去看看 chromium 最新的源代码，<a href="https://link.juejin.cn/?target=https://chromium.googlesource.com/chromium/src/+/refs/heads/master/ipc/">IPC源码地址</a>及<a href="https://link.juejin.cn/?target=https://blog.csdn.net/Luoshengyang/article/details/47822689">Chromium IPC源码解析文章</a>。</p>
<p>总的来说就是利用<code>Unix Domain Socket</code>套接字，配合事件驱动的高性能网络并发库<code>libevent</code>完成进程的 IPC 过程。</p>
<p>好，现在数据传递给了浏览器主进程，主进程接收到后，才真正地发出相应的网络请求。</p>
<p>在服务端处理完数据后，将响应返回，主进程检查到跨域，且没有cors(后面会详细说)响应头，将响应体全部丢掉，并不会发送给渲染进程。这就达到了拦截数据的目的。</p>
<p>接下来我们来说一说解决跨域问题的几种方案。</p>
<h3 id="CORS"><a href="#CORS" class="headerlink" title="CORS"></a>CORS</h3><p>CORS 其实是 W3C 的一个标准，全称是<code>跨域资源共享</code>。它需要浏览器和服务器的共同支持，具体来说，非 IE 和 IE10 以上支持CORS，服务器需要附加特定的响应头，后面具体拆解。不过在弄清楚 CORS 的原理之前，我们需要清楚两个概念: <strong>简单请求</strong>和<strong>非简单请求</strong>。</p>
<p>浏览器根据请求方法和请求头的特定字段，将请求做了一下分类，具体来说规则是这样，凡是满足下面条件的属于<strong>简单请求</strong>:</p>
<ul>
<li>请求方法为 GET、POST 或者 HEAD</li>
<li>请求头的取值范围: Accept、Accept-Language、Content-Language、Content-Type(只限于三个值<code>application/x-www-form-urlencoded</code>、<code>multipart/form-data</code>、<code>text/plain</code>)</li>
</ul>
<p>浏览器画了这样一个圈，在这个圈里面的就是<strong>简单请求</strong>, 圈外面的就是<strong>非简单请求</strong>，然后针对这两种不同的请求进行不同的处理。</p>
<h4 id="简单请求"><a href="#简单请求" class="headerlink" title="简单请求"></a>简单请求</h4><p>请求发出去之前，浏览器做了什么？</p>
<p>它会自动在请求头当中，添加一个<code>Origin</code>字段，用来说明请求来自哪个<code>源</code>。服务器拿到请求之后，在回应时对应地添加<code>Access-Control-Allow-Origin</code>字段，如果<code>Origin</code>不在这个字段的范围中，那么浏览器就会将响应拦截。</p>
<p>因此，<code>Access-Control-Allow-Origin</code>字段是服务器用来决定浏览器是否拦截这个响应，这是必需的字段。与此同时，其它一些可选的功能性的字段，用来描述如果不会拦截，这些字段将会发挥各自的作用。</p>
<p><strong>Access-Control-Allow-Credentials</strong>。这个字段是一个布尔值，表示是否允许发送 Cookie，对于跨域请求，浏览器对这个字段默认值设为 false，而如果需要拿到浏览器的 Cookie，需要添加这个响应头并设为<code>true</code>, 并且在前端也需要设置<code>withCredentials</code>属性:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">let xhr = new XMLHttpRequest();</span><br><span class="line">xhr.withCredentials = true;</span><br></pre></td></tr></table></figure>

<p><strong>Access-Control-Expose-Headers</strong>。这个字段是给 XMLHttpRequest 对象赋能，让它不仅可以拿到基本的 6 个响应头字段（包括<code>Cache-Control</code>、<code>Content-Language</code>、<code>Content-Type</code>、<code>Expires</code>、<code>Last-Modified</code>和<code>Pragma</code>）, 还能拿到这个字段声明的<strong>响应头字段</strong>。比如这样设置:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Access-Control-Expose-Headers: aaa</span><br></pre></td></tr></table></figure>

<p>那么在前端可以通过 <code>XMLHttpRequest.getResponseHeader(&#39;aaa&#39;)</code> 拿到 <code>aaa</code> 这个字段的值。</p>
<h4 id="非简单请求"><a href="#非简单请求" class="headerlink" title="非简单请求"></a>非简单请求</h4><p>非简单请求相对而言会有些不同，体现在两个方面: <strong>预检请求</strong>和<strong>响应字段</strong>。</p>
<p>我们以 PUT 方法为例。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var url = &#x27;http://xxx.com&#x27;;</span><br><span class="line">var xhr = new XMLHttpRequest();</span><br><span class="line">xhr.open(&#x27;PUT&#x27;, url, true);</span><br><span class="line">xhr.setRequestHeader(&#x27;X-Custom-Header&#x27;, &#x27;xxx&#x27;);</span><br><span class="line">xhr.send();</span><br></pre></td></tr></table></figure>

<p>当这段代码执行后，首先会发送<strong>预检请求</strong>。这个预检请求的请求行和请求体是下面这个格式:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">OPTIONS / HTTP/1.1</span><br><span class="line">Origin: 当前地址</span><br><span class="line">Host: xxx.com</span><br><span class="line">Access-Control-Request-Method: PUT</span><br><span class="line">Access-Control-Request-Headers: X-Custom-Header</span><br></pre></td></tr></table></figure>

<p>预检请求的方法是<code>OPTIONS</code>，同时会加上<code>Origin</code>源地址和<code>Host</code>目标地址，这很简单。同时也会加上两个关键的字段:</p>
<ul>
<li>Access-Control-Request-Method, 列出 CORS 请求用到哪个HTTP方法</li>
<li>Access-Control-Request-Headers，指定 CORS 请求将要加上什么请求头</li>
</ul>
<p>这是<code>预检请求</code>。接下来是<strong>响应字段</strong>，响应字段也分为两部分，一部分是对于<strong>预检请求</strong>的响应，一部分是对于 <strong>CORS 请求</strong>的响应。</p>
<p><strong>预检请求的响应</strong>。如下面的格式:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Access-Control-Allow-Origin: *</span><br><span class="line">Access-Control-Allow-Methods: GET, POST, PUT</span><br><span class="line">Access-Control-Allow-Headers: X-Custom-Header</span><br><span class="line">Access-Control-Allow-Credentials: true</span><br><span class="line">Access-Control-Max-Age: 1728000</span><br><span class="line">Content-Type: text/html; charset=utf-8</span><br><span class="line">Content-Encoding: gzip</span><br><span class="line">Content-Length: 0</span><br></pre></td></tr></table></figure>

<p>其中有这样几个关键的<strong>响应头字段</strong>:</p>
<ul>
<li>Access-Control-Allow-Origin: 表示可以允许请求的源，可以填具体的源名，也可以填<code>*</code>表示允许任意源请求。</li>
<li>Access-Control-Allow-Methods: 表示允许的请求方法列表。</li>
<li>Access-Control-Allow-Credentials: 简单请求中已经介绍。</li>
<li>Access-Control-Allow-Headers: 表示允许发送的请求头字段</li>
<li>Access-Control-Max-Age: 预检请求的有效期，在此期间，不用发出另外一条预检请求。</li>
</ul>
<p>在预检请求的响应返回后，如果请求不满足响应头的条件，则触发<code>XMLHttpRequest</code>的<code>onerror</code>方法，当然后面真正的<strong>CORS请求</strong>也不会发出去了。</p>
<p><strong>CORS 请求的响应</strong>。绕了这么一大转，到了真正的 CORS 请求就容易多了，现在它和<strong>简单请求</strong>的情况是一样的。浏览器自动加上<code>Origin</code>字段，服务端响应头返回<strong>Access-Control-Allow-Origin</strong>。可以参考以上简单请求部分的内容。</p>
<h3 id="JSONP"><a href="#JSONP" class="headerlink" title="JSONP"></a>JSONP</h3><p>虽然<code>XMLHttpRequest</code>对象遵循同源政策，但是<code>script</code>标签不一样，它可以通过 src 填上目标地址从而发出 GET 请求，实现跨域请求并拿到响应。这也就是 JSONP 的原理，接下来我们就来封装一个 JSONP:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> jsonp = <span class="function">(<span class="params">&#123; url, params, callbackName &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> generateURL = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> dataStr = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">let</span> key <span class="keyword">in</span> params) &#123;</span><br><span class="line">      dataStr += <span class="string">`<span class="subst">$&#123;key&#125;</span>=<span class="subst">$&#123;params[key]&#125;</span>&amp;`</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    dataStr += <span class="string">`callback=<span class="subst">$&#123;callbackName&#125;</span>`</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`<span class="subst">$&#123;url&#125;</span>?<span class="subst">$&#123;dataStr&#125;</span>`</span>;</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 初始化回调函数名称</span></span><br><span class="line">    callbackName = callbackName || <span class="built_in">Math</span>.random().toString.replace(<span class="string">&#x27;,&#x27;</span>, <span class="string">&#x27;&#x27;</span>); </span><br><span class="line">    <span class="comment">// 创建 script 元素并加入到当前文档中</span></span><br><span class="line">    <span class="keyword">let</span> scriptEle = <span class="built_in">document</span>.createElement(<span class="string">&#x27;script&#x27;</span>);</span><br><span class="line">    scriptEle.src = generateURL();</span><br><span class="line">    <span class="built_in">document</span>.body.appendChild(scriptEle);</span><br><span class="line">    <span class="comment">// 绑定到 window 上，为了后面调用</span></span><br><span class="line">    <span class="built_in">window</span>[callbackName] = <span class="function">(<span class="params">data</span>) =&gt;</span> &#123;</span><br><span class="line">      resolve(data);</span><br><span class="line">      <span class="comment">// script 执行完了，成为无用元素，需要清除</span></span><br><span class="line">      <span class="built_in">document</span>.body.removeChild(scriptEle);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当然在服务端也会有响应的操作, 以 express 为例:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> app = express()</span><br><span class="line">app.get(<span class="string">&#x27;/&#x27;</span>, <span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> &#123; a, b, callback &#125; = req.query</span><br><span class="line">  <span class="built_in">console</span>.log(a); <span class="comment">// 1</span></span><br><span class="line">  <span class="built_in">console</span>.log(b); <span class="comment">// 2</span></span><br><span class="line">  <span class="comment">// 注意哦，返回给script标签，浏览器直接把这部分字符串执行</span></span><br><span class="line">  res.end(<span class="string">`<span class="subst">$&#123;callback&#125;</span>(&#x27;数据包&#x27;)`</span>);</span><br><span class="line">&#125;)</span><br><span class="line">app.listen(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure>

<p>前端这样简单地调用一下就好了:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">jsonp(&#123;</span><br><span class="line">  <span class="attr">url</span>: <span class="string">&#x27;http://localhost:3000&#x27;</span>,</span><br><span class="line">  <span class="attr">params</span>: &#123; </span><br><span class="line">    <span class="attr">a</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="attr">b</span>: <span class="number">2</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;).then(<span class="function"><span class="params">data</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// 拿到数据进行处理</span></span><br><span class="line">  <span class="built_in">console</span>.log(data); <span class="comment">// 数据包</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p>和<code>CORS</code>相比，JSONP 最大的优势在于兼容性好，IE 低版本不能使用 CORS 但可以使用 JSONP，缺点也很明显，请求方法单一，只支持 GET 请求。</p>
<h3 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h3><p>Nginx 是一种高性能的<code>反向代理</code>服务器，可以用来轻松解决跨域问题。</p>
<p>what？反向代理？我给你看一张图你就懂了。</p>
<p><img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/3/22/170ffd97d0b1cf15~tplv-t2oaga2asx-watermark.awebp" alt="img"></p>
<p>正向代理帮助客户端<strong>访问</strong>客户端自己访问不到的服务器，然后将结果返回给客户端。</p>
<p>反向代理拿到客户端的请求，将请求转发给其他的服务器，主要的场景是维持服务器集群的<strong>负载均衡</strong>，换句话说，反向代理帮<strong>其它的服务器</strong>拿到请求，然后选择一个合适的服务器，将请求转交给它。</p>
<p>因此，两者的区别就很明显了，正向代理服务器是帮<strong>客户端</strong>做事情，而反向代理服务器是帮其它的<strong>服务器</strong>做事情。</p>
<p>好了，那 Nginx 是如何来解决跨域的呢？</p>
<p>比如说现在客户端的域名为<strong>client.com</strong>，服务器的域名为<strong>server.com</strong>，客户端向服务器发送 Ajax 请求，当然会跨域了，那这个时候让 Nginx 登场了，通过下面这个配置:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">  listen  80;</span><br><span class="line">  server_name  client.com;</span><br><span class="line">  location /api &#123;</span><br><span class="line">    proxy_pass server.com;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Nginx 相当于起了一个跳板机，这个跳板机的域名也是<code>client.com</code>，让客户端首先访问 <code>client.com/api</code>，这当然没有跨域，然后 Nginx 服务器作为反向代理，将请求转发给<code>server.com</code>，当响应返回时又将响应给到客户端，这就完成整个跨域请求的过程。</p>

  </div>
</article>



        
          <div id="footer-post-container">
  <div id="footer-post">

    <div id="nav-footer" style="display: none">
      <ul>
         
          <li><a href="/">首页</a></li>
         
          <li><a href="/tags/">标签</a></li>
         
          <li><a href="/categories/">分类</a></li>
         
          <li><a href="/archives/">归档</a></li>
         
          <li><a target="_blank" rel="noopener" href="http://github.com/twitzz">项目</a></li>
         
          <li><a href="/about/">关于</a></li>
         
          <li><a href="/search/">搜索</a></li>
        
      </ul>
    </div>

    <div id="toc-footer" style="display: none">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84"><span class="toc-number">1.</span> <span class="toc-text">http报文结构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B5%B7%E5%A7%8B%E8%A1%8C"><span class="toc-number">1.1.</span> <span class="toc-text">起始行</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%B4%E9%83%A8"><span class="toc-number">1.2.</span> <span class="toc-text">头部</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A9%BA%E8%A1%8C"><span class="toc-number">1.3.</span> <span class="toc-text">空行</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9E%E4%BD%93"><span class="toc-number">1.4.</span> <span class="toc-text">实体</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E8%AF%B7%E6%B1%82%E6%96%B9%E6%B3%95"><span class="toc-number">2.</span> <span class="toc-text">http请求方法</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#GET%E5%92%8CPOST%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-number">2.1.</span> <span class="toc-text">GET和POST的区别</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#URI"><span class="toc-number">3.</span> <span class="toc-text">URI</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#URI%E7%BB%93%E6%9E%84"><span class="toc-number">3.1.</span> <span class="toc-text">URI结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#URI-%E7%BC%96%E7%A0%81"><span class="toc-number">3.2.</span> <span class="toc-text">URI 编码</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E7%8A%B6%E6%80%81%E7%A0%81"><span class="toc-number">4.</span> <span class="toc-text">http状态码</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1xx"><span class="toc-number">4.1.</span> <span class="toc-text">1xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2xx"><span class="toc-number">4.2.</span> <span class="toc-text">2xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3xx"><span class="toc-number">4.3.</span> <span class="toc-text">3xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4xx"><span class="toc-number">4.4.</span> <span class="toc-text">4xx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5xx"><span class="toc-number">4.5.</span> <span class="toc-text">5xx</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E7%9A%84%E7%89%B9%E7%82%B9"><span class="toc-number">5.</span> <span class="toc-text">http的特点</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E7%9A%84%E7%BC%BA%E7%82%B9"><span class="toc-number">6.</span> <span class="toc-text">http的缺点</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A0%E7%8A%B6%E6%80%81"><span class="toc-number">6.1.</span> <span class="toc-text">无状态</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%98%8E%E6%96%87%E4%BC%A0%E8%BE%93"><span class="toc-number">6.2.</span> <span class="toc-text">明文传输</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%98%9F%E5%A4%B4%E9%98%BB%E5%A1%9E"><span class="toc-number">6.3.</span> <span class="toc-text">队头阻塞</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Accept%E7%B3%BB%E5%88%97%E5%AD%97%E6%AE%B5"><span class="toc-number">7.</span> <span class="toc-text">Accept系列字段</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F"><span class="toc-number">7.1.</span> <span class="toc-text">数据格式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%8B%E7%BC%A9%E6%96%B9%E5%BC%8F"><span class="toc-number">7.2.</span> <span class="toc-text">压缩方式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%94%AF%E6%8C%81%E8%AF%AD%E8%A8%80"><span class="toc-number">7.3.</span> <span class="toc-text">支持语言</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AD%97%E7%AC%A6%E9%9B%86"><span class="toc-number">7.4.</span> <span class="toc-text">字符集</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%BC%A0%E8%BE%93%E5%AE%9A%E9%95%BF%E5%92%8C%E4%B8%8D%E5%AE%9A%E9%95%BF%E7%9A%84%E6%95%B0%E6%8D%AE"><span class="toc-number">8.</span> <span class="toc-text">http传输定长和不定长的数据</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E9%95%BF%E5%8C%85%E4%BD%93"><span class="toc-number">8.1.</span> <span class="toc-text">定长包体</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%8D%E5%AE%9A%E9%95%BF%E5%8C%85%E4%BD%93"><span class="toc-number">8.2.</span> <span class="toc-text">不定长包体</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%B8%AD%E5%A4%84%E7%90%86%E5%A4%A7%E6%96%87%E4%BB%B6%E7%9A%84%E4%BC%A0%E8%BE%93"><span class="toc-number">9.</span> <span class="toc-text">http中处理大文件的传输</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E6%94%AF%E6%8C%81"><span class="toc-number">9.1.</span> <span class="toc-text">如何支持</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Range%E5%AD%97%E6%AE%B5%E6%8B%86%E8%A7%A3"><span class="toc-number">9.2.</span> <span class="toc-text">Range字段拆解</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E6%AE%B5%E6%95%B0%E6%8D%AE"><span class="toc-number">9.3.</span> <span class="toc-text">单段数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E6%AE%B5%E6%95%B0%E6%8D%AE"><span class="toc-number">9.4.</span> <span class="toc-text">多段数据</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%B8%AD%E5%A4%84%E7%90%86%E8%A1%A8%E5%8D%95%E6%95%B0%E6%8D%AE%E7%9A%84%E6%8F%90%E4%BA%A4"><span class="toc-number">10.</span> <span class="toc-text">http中处理表单数据的提交</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#application-x-www-form-urlencoded"><span class="toc-number">10.1.</span> <span class="toc-text">application&#x2F;x-www-form-urlencoded</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#multipart-form-data"><span class="toc-number">10.2.</span> <span class="toc-text">multipart&#x2F;form-data</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%8F%E7%BB%93"><span class="toc-number">10.3.</span> <span class="toc-text">小结</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E9%98%9F%E5%A4%B4%E9%98%BB%E5%A1%9E"><span class="toc-number">11.</span> <span class="toc-text">解决队头阻塞</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B9%B6%E5%8F%91%E8%BF%9E%E6%8E%A5"><span class="toc-number">11.1.</span> <span class="toc-text">并发连接</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9F%9F%E5%90%8D%E5%88%86%E7%89%87"><span class="toc-number">11.2.</span> <span class="toc-text">域名分片</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%BB%A3%E7%90%86"><span class="toc-number">12.</span> <span class="toc-text">http代理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%9B%B8%E5%85%B3%E5%A4%B4%E9%83%A8%E5%AD%97%E6%AE%B5"><span class="toc-number">12.1.</span> <span class="toc-text">相关头部字段</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Via"><span class="toc-number">12.1.1.</span> <span class="toc-text">Via</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#X-Forwarded-For"><span class="toc-number">12.1.2.</span> <span class="toc-text">X-Forwarded-For</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#X-Real-IP"><span class="toc-number">12.1.3.</span> <span class="toc-text">X-Real-IP</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#X-Forwarded-For%E4%BA%A7%E7%94%9F%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-number">12.2.</span> <span class="toc-text">X-Forwarded-For产生的问题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#http%E4%B8%AD%E7%9A%84%E4%BB%A3%E7%90%86%E7%BC%93%E5%AD%98"><span class="toc-number">13.</span> <span class="toc-text">http中的代理缓存</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BA%A7%E7%94%9F%E4%BB%A3%E7%90%86%E7%BC%93%E5%AD%98%EF%BC%9F"><span class="toc-number">13.1.</span> <span class="toc-text">为什么产生代理缓存？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%BA%90%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E7%BC%93%E5%AD%98%E6%8E%A7%E5%88%B6"><span class="toc-number">13.2.</span> <span class="toc-text">源服务器的缓存控制</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#private-%E5%92%8C-public"><span class="toc-number">13.2.1.</span> <span class="toc-text">private 和 public</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#proxy-revalidate"><span class="toc-number">13.2.2.</span> <span class="toc-text">proxy-revalidate</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#s-maxage"><span class="toc-number">13.2.3.</span> <span class="toc-text">s-maxage</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E7%BC%93%E5%AD%98%E6%8E%A7%E5%88%B6"><span class="toc-number">13.3.</span> <span class="toc-text">客户端的缓存控制</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#max-stale-%E5%92%8C-min-fresh"><span class="toc-number">13.3.1.</span> <span class="toc-text">max-stale 和 min-fresh</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#only-if-cached"><span class="toc-number">13.3.2.</span> <span class="toc-text">only-if-cached</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%B7%A8%E5%9F%9F%E7%9B%B8%E5%85%B3"><span class="toc-number">14.</span> <span class="toc-text">跨域相关</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E8%B7%A8%E5%9F%9F"><span class="toc-number">14.1.</span> <span class="toc-text">什么是跨域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#CORS"><span class="toc-number">14.2.</span> <span class="toc-text">CORS</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%AE%80%E5%8D%95%E8%AF%B7%E6%B1%82"><span class="toc-number">14.2.1.</span> <span class="toc-text">简单请求</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%9D%9E%E7%AE%80%E5%8D%95%E8%AF%B7%E6%B1%82"><span class="toc-number">14.2.2.</span> <span class="toc-text">非简单请求</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JSONP"><span class="toc-number">14.3.</span> <span class="toc-text">JSONP</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Nginx"><span class="toc-number">14.4.</span> <span class="toc-text">Nginx</span></a></li></ol></li></ol>
    </div>

    <div id="share-footer" style="display: none">
      <ul>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.facebook.com/sharer.php?u=https://danyuan30.gitee.io/2022/03/05/network/http/"><i class="fab fa-facebook fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://twitter.com/share?url=https://danyuan30.gitee.io/2022/03/05/network/http/&text=http总结"><i class="fab fa-twitter fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.linkedin.com/shareArticle?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-linkedin fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://pinterest.com/pin/create/bookmarklet/?url=https://danyuan30.gitee.io/2022/03/05/network/http/&is_video=false&description=http总结"><i class="fab fa-pinterest fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=http总结&body=Check out this article: https://danyuan30.gitee.io/2022/03/05/network/http/"><i class="fas fa-envelope fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://getpocket.com/save?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://reddit.com/submit?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-reddit fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.stumbleupon.com/submit?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-stumbleupon fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://digg.com/submit?url=https://danyuan30.gitee.io/2022/03/05/network/http/&title=http总结"><i class="fab fa-digg fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.tumblr.com/share/link?url=https://danyuan30.gitee.io/2022/03/05/network/http/&name=http总结&description="><i class="fab fa-tumblr fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=https://danyuan30.gitee.io/2022/03/05/network/http/&t=http总结"><i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i></a></li>
</ul>

    </div>

    <div id="actions-footer">
        <a id="menu" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;"><i class="fas fa-bars fa-lg" aria-hidden="true"></i> 菜单</a>
        <a id="toc" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;"><i class="fas fa-list fa-lg" aria-hidden="true"></i> 目录</a>
        <a id="share" class="icon" href="#" onclick="$('#share-footer').toggle();return false;"><i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> 分享</a>
        <a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> 返回顶部</a>
    </div>

  </div>
</div>

        
        <footer id="footer">
  <div class="footer-left">
    Copyright &copy;
    
    
    2021-2022
    柯里的语法糖
  </div>
  <div class="footer-right">
    <nav>
      <ul>
        <!--
       --><li><a href="/">首页</a></li><!--
     --><!--
       --><li><a href="/tags/">标签</a></li><!--
     --><!--
       --><li><a href="/categories/">分类</a></li><!--
     --><!--
       --><li><a href="/archives/">归档</a></li><!--
     --><!--
       --><li><a target="_blank" rel="noopener" href="http://github.com/twitzz">项目</a></li><!--
     --><!--
       --><li><a href="/about/">关于</a></li><!--
     --><!--
       --><li><a href="/search/">搜索</a></li><!--
     -->
      </ul>
    </nav>
  </div>
</footer>

    </div>
    <!-- styles -->



  <link rel="preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'"/>


    <!-- jquery -->
 
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" crossorigin="anonymous"></script> 




<!-- clipboard -->

  
    <script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.7/clipboard.min.js" crossorigin="anonymous"></script> 
  
  <script type="text/javascript">
  $(function() {
    // copy-btn HTML
    var btn = "<span class=\"btn-copy tooltipped tooltipped-sw\" aria-label=\"复制到粘贴板!\">";
    btn += '<i class="far fa-clone"></i>';
    btn += '</span>'; 
    // mount it!
    $(".highlight table").before(btn);
    var clip = new ClipboardJS('.btn-copy', {
      text: function(trigger) {
        return Array.from(trigger.nextElementSibling.querySelectorAll('.code')).reduce((str,it)=>str+it.innerText+'\n','')
      }
    });
    clip.on('success', function(e) {
      e.trigger.setAttribute('aria-label', "复制成功!");
      e.clearSelection();
    })
  })
  </script>


<script src="/js/main.js"></script>

<!-- search -->

<!-- Google Analytics -->

<!-- Baidu Analytics -->

<!-- Cloudflare Analytics -->

<!-- Umami Analytics -->

<!-- Disqus Comments -->

<!-- utterances Comments -->

</body>
</html>
